﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Activators;
using MugenInjection.Bindings;
using MugenInjection.Bindings.Builders;
using MugenInjection.Core;
using MugenInjection.Delegates;
using MugenInjection.Exceptions;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Parameters;
using MugenInjection.Scope;
using MugenInjection.Syntax;
using MugenInjection.Syntax.Method;
using Should;
using TestModels.Class;
using TestModels.Interface;
using Action = System.Action;

namespace MugenInjectionTest.Bindings
{
    [TestClass]
    public class BindingBuilderMethodTest : TestBase
    {
        [TestMethod]
        public void CreateBindingTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty), typeof(I1) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass());

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ShouldNotBeNull();
                methodBinding.Services.Contains(typeof(IEmpty)).ShouldBeTrue();
                methodBinding.Services.Contains(typeof(I1)).ShouldBeTrue();
                methodBinding.ActivatingActions.ShouldBeEmpty();
                methodBinding.ActivatedActions.Count.ShouldEqual(2);
                methodBinding.ActivatedActions.Contains(InjectorUtilsInternal.ActivateInitializable).ShouldBeTrue();
                methodBinding.ActivatedActions.Contains(InjectorUtilsInternal.ActivateStartable).ShouldBeTrue();

                methodBinding.DeactivatedActions.ShouldBeEmpty();
                methodBinding.TryDisposeObject.ShouldBeFalse();
                methodBinding.Parameters.ShouldBeEmpty();
                methodBinding.Priority.ShouldEqual(BindingPriority.Standard);

                Action action = () => new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty), typeof(IEmpty) }, injector);
                action.ShouldThrow<ArgumentException>();
            }
        }

        [TestMethod]
        public void WithConditionWhenIntoTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenInto(typeof(MemberInfo));

                Action action = () => syntax.WhenInto(typeof(object));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(MemberInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenIntoGenericTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax bindingSyntax = bindingBuilder;
                var syntax = bindingSyntax.ToMethod(context => new SimpleClass());
                syntax.WhenInto<MemberInfo>();

                Action action = () => syntax.WhenInto(typeof(object));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(MemberInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenIntoIsAssignableTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenIntoIsAssignable(typeof(MemberInfo));

                Action action = () => syntax.WhenIntoIsAssignable(typeof(object));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(MemberInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenIntoIsAssignableGenericTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenIntoIsAssignable<MemberInfo>();

                Action action = () => syntax.WhenIntoIsAssignable(typeof(object));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(MemberInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenNamespaceEqualTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenNamespaceEqual("System");

                Action action = () => syntax.WhenNamespaceEqual("System");
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(object).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenClassHasAttributeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenClassHasAttribute(typeof(SimpleAttribute));

                Action action = () => syntax.WhenClassHasAttribute(typeof(SimpleAttribute));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(ClassHasAttribute).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenClassHasAttributeGenericTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.WhenClassHasAttribute<SimpleAttribute>();

                Action action = () => syntax.WhenClassHasAttribute(typeof(SimpleAttribute));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(PropertyInfo).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(ClassHasAttribute).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);
            }
        }

        [TestMethod]
        public void WithConditionWhenTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.When(context => context.TypeInto == typeof(object));

                Action action = () => syntax.When(context => context.TypeInto == typeof(object));
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeFalse();

                methodBinding.CanResolve(new BindingContext(typeof(IEmpty), typeof(object).GetTypeInfo(), null,
                                                              injector, injector,
                                                              InjectorUtilsInternal.EmptyInjectionParams,
                                                              InjectorUtilsInternal.ReadOnlyEmptyDictionary))
                               .ShouldBeTrue();
                methodBinding.Priority.ShouldEqual(BindingPriority.Normal);
            }
        }

        [TestMethod]
        public void PriorityTest()
        {
            using (var injector = GetInjector())
            {
                //Test priority
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).SetStandardPriority();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Priority.ShouldEqual(BindingPriority.Standard);

                //Low
                bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).SetLowPriority();

                methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Priority.ShouldEqual(BindingPriority.Low);

                //Normal
                bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).SetNormalPriority();

                methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Priority.ShouldEqual(BindingPriority.Normal);

                //High
                bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).SetHighPriority();

                methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Priority.ShouldEqual(BindingPriority.High);

                //Custom
                bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.SetPriority(BindingPriority.High);

                methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Priority.ShouldEqual(BindingPriority.High);

                Action action = () => syntax.SetNormalPriority();
                action.ShouldThrow<InvalidBindingException>();
            }
        }

        [TestMethod]
        public void NamedBindingTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;

                var syntax = binding.ToMethod(context => new SimpleClass()).NamedBinding("NamedBinding");
                Action action = () => syntax.NamedBinding("Test");
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                var dictionary = new Dictionary<string, object>();
                var bindingContext = new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null, injector, injector, InjectorUtilsInternal.EmptyInjectionParams, dictionary);
                methodBinding.CanResolve(bindingContext).ShouldBeFalse();
                SpecialParameterKeys.SetKeyedBindingParameter(dictionary, "NamedBinding");
                methodBinding.CanResolve(bindingContext).ShouldBeTrue();
            }
        }

        [TestMethod]
        public void KeyedBindingTest()
        {
            var key = new object();
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;

                var syntax = binding.ToMethod(context => new SimpleClass()).KeyedBinding(key);
                Action action = () => syntax.KeyedBinding(key);
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                var dictionary = new Dictionary<string, object>();
                var bindingContext = new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null, injector, injector, InjectorUtilsInternal.EmptyInjectionParams, dictionary);
                methodBinding.CanResolve(bindingContext).ShouldBeFalse();
                SpecialParameterKeys.SetKeyedBindingParameter(dictionary, key);
                methodBinding.CanResolve(bindingContext).ShouldBeTrue();
            }
        }

        [TestMethod]
        public void NamedBindingWithConditionTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;

                var syntax = binding.ToMethod(context => new SimpleClass()).WhenInto<SimpleClass>().NamedBinding("NamedBinding");
                Action action = () => syntax.NamedBinding("Test");
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                var dictionary = new Dictionary<string, object>();
                var bindingContext = new BindingContext(typeof(IEmpty), typeof(IEmpty).GetTypeInfo(), null, injector, injector, InjectorUtilsInternal.EmptyInjectionParams, dictionary);
                methodBinding.CanResolve(bindingContext).ShouldBeFalse();
                SpecialParameterKeys.SetKeyedBindingParameter(dictionary, "NamedBinding");
                methodBinding.CanResolve(bindingContext).ShouldBeFalse();

                bindingContext = new BindingContext(typeof(IEmpty), typeof(SimpleClass).GetTypeInfo(), null, injector, injector, InjectorUtilsInternal.EmptyInjectionParams, dictionary);
                methodBinding.CanResolve(bindingContext).ShouldBeTrue();
            }
        }

        [TestMethod]
        public void TryDisposeObjectTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass()).TryDisposeObjects();
                Action action = () => syntax.TryDisposeObjects();
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.TryDisposeObject.ShouldBeTrue();
            }
        }

        [TestMethod]
        public void WithSettingTest()
        {
            using (var injector = GetInjector())
            {
                var settingObj = new object();
                var settingObj1 = new object();

                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).WithSetting("test", settingObj).WithSetting("test1", settingObj1);

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Settings.Get("test").ShouldEqual(settingObj);
                methodBinding.Settings.Get("test1").ShouldEqual(settingObj1);
            }
        }

        [TestMethod]
        public void BindingBuilderTest()
        {
            using (var injector = GetInjector())
            {
                bool isBuilded = false;
                bool isBuilding = false;
                var bindingBuilder = new BindingBuilder<I1, object, object, object>(new[] { typeof(I1) }, injector).ToMethod(context => new SimpleClass());

                bindingBuilder.Building += builder =>
                {
                    isBuilded.ShouldBeFalse();
                    builder.ShouldNotBeNull();
                    isBuilding = true;
                    return builder;
                };
                bindingBuilder.Builded += builder =>
                {
                    var binding = builder.Build();
                    isBuilded = true;
                    isBuilding.ShouldBeTrue();
                    binding.ShouldBeType<MethodBinding>();
                    return binding;
                };

                bindingBuilder.Services.Contains(typeof(I1)).ShouldBeTrue();
                bindingBuilder.Injector.ShouldEqual(injector);

                (bindingBuilder.Build() is MethodBinding).ShouldBeTrue();
                isBuilded.ShouldBeTrue();
            }
        }

        [TestMethod]
        public void CallbackEventsTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                BindingActivatedDelegate<object> activated = (IBinding binding1, IBindingContext context, ref object instance) => { };
                var activating = new Action<IBindingContext>(o => { });
                var disposed = new Action<IBinding>(o => { });
#if NET2
                var onReleaseObject = new MugenInjection.Delegates.Action<object, bool>((o, b) => { });
#else
                var onReleaseObject = new Action<object, bool>((o, b) => { });
#endif

                IMethodCallbackObjectUseWithSyntax objectSyntax = binding
                    .ToMethod(context => new SimpleClass())
                    .InSingletonScope()
                    .OnActivated(activated)
                    .OnDisposed(disposed)
                    .OnActivating(activating)
                    .OnReleaseObject(onReleaseObject);

                var methodBinding = (MethodBinding)bindingBuilder.Build();

                methodBinding.ActivatedActions.Contains(activated).ShouldBeTrue();
                methodBinding.ActivatingActions.Contains(activating).ShouldBeTrue();
                methodBinding.DeactivatedActions.Contains(disposed).ShouldBeTrue();
                methodBinding.ScopeLifecycle.ReleaseObjectActions.Contains(onReleaseObject).ShouldBeTrue();
            }
        }

        [TestMethod]
        public void ConstructorParametersTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = (IGenericBindingSyntax<IEmpty>)new BindingBuilder<IEmpty, object, object, object>(new[] { typeof(IEmpty) }, injector);
                var binding = bindingBuilder;
#if NOEXPRESSION
                binding.ToMethod(context => new SimpleClass()).WithConstructorArgument("Test", new object());
#else
                binding.ToMethod(context => new SimpleClass())
                 .WithConstructorArguments(context => new SimpleClass("test"));
#endif
                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Parameters.Count.ShouldEqual(1);
                methodBinding.Parameters.OfType<ConstructorParameter>().Any().ShouldBeTrue();

                bindingBuilder = new BindingBuilder<IEmpty, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;
#if NOEXPRESSION
                var syntax = binding.ToMethod(context => new SimpleClass()).WithConstructorArgument("Test", context => new object());
                Action action = () => syntax.WithConstructorArgument("Test", new object());
#else
                var syntax = binding.ToMethod(context => new SimpleClass()).WithConstructorArguments(context => new SimpleClass("test"));
                Action action = () => syntax.WithConstructorArguments(context => new SimpleClass("string"));
#endif
                action.ShouldThrow<InvalidBindingException>();
            }
        }

        [TestMethod]
        public void MethodParametersTest()
        {
            using (var injector = GetInjector())
            {
                //Method params
                IGenericBindingSyntax<SimpleClass> bindingBuilder = new BindingBuilder<SimpleClass, object, object, object>(new[] { typeof(IEmpty) }, injector);
                var binding = bindingBuilder;

#if NOEXPRESSION
                binding.ToMethod(context => new SimpleClass()).WithMethodArgument("MethodTest", "Test", new object());
#else
                binding.ToMethod(context => new SimpleClass()).WithMethodArguments((context, @class) => @class.Method("test"));
#endif
                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Parameters.Count.ShouldEqual(1);
                methodBinding.Parameters.OfType<MethodParameter>().Any().ShouldBeTrue();

                bindingBuilder = new BindingBuilder<SimpleClass, object, object, object>(new[] { typeof(IEmpty) }, injector);
                binding = bindingBuilder;

#if NOEXPRESSION
                var syntax = binding.ToMethod(context => new SimpleClass()).WithMethodArgument("MethodTest", "Test", context => new object());
                Action action = () => syntax.WithMethodArgument("MethodTest", "Test", new object());
#else
                var syntax = binding.ToMethod(context => new SimpleClass()).WithMethodArguments((context, @class) => @class.Method("test"));
                Action action = () => syntax.WithMethodArguments((context, @class) => @class.Method("test"));
#endif
                action.ShouldThrow<InvalidBindingException>();
            }
        }

        [TestMethod]
        public void PropertyParametersTest()
        {
            using (var injector = GetInjector())
            {
                IGenericBindingSyntax<SimpleClass> bindingBuilder = new BindingBuilder<SimpleClass, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                var binding = bindingBuilder;
#if NOEXPRESSION
                binding.ToMethod(context => new SimpleClass()).WithPropertyValue("Test", new object());
#else
                binding.ToMethod(context => new SimpleClass()).WithPropertyValue(@class => @class.Property, "tset");
#endif
                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Parameters.Count.ShouldEqual(1);
                methodBinding.Parameters.OfType<PropertyParameter>().Any().ShouldBeTrue();

                bindingBuilder = new BindingBuilder<SimpleClass, object, object, object>(new[] { typeof(IEmpty) },
                                                                                    injector);
                binding = bindingBuilder;
#if NOEXPRESSION
                var syntax = binding.ToMethod(context => new SimpleClass())
                                    .WithPropertyValue("Test", context => new object());
                Action action = () => syntax.WithPropertyValue("Test", new object());
#else
                var syntax = binding.ToMethod(context => new SimpleClass()).WithPropertyValue(@class => @class.Property, "test");
                Action action = () => syntax.WithPropertyValue(@class => @class.Property, "test");
#endif
                action.ShouldThrow<InvalidBindingException>();
            }
        }

        [TestMethod]
        public void CustomParametersTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<IEmpty, object, object, object>(new[] { typeof(IEmpty) }, injector);
                var binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).WithParameter(new PropertyParameter("Test", context => new object()));

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Parameters.Count.ShouldEqual(1);
                methodBinding.Parameters.OfType<PropertyParameter>().Any().ShouldBeTrue();
            }
        }

        [TestMethod]
        public void TransientScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InTransientScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<TransientScopeLifecycle>();
            }
        }

        [TestMethod]
        public void SingletonScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InSingletonScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<SingletonScopeLifecycle>();
            }
        }

        [TestMethod]
        public void ThreadScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InThreadScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<ThreadScopeLifecycle>();
            }
        }

        [TestMethod]
        public void ManagedScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InManagedScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<ManagedScopeLifecycle>();
            }
        }

        [TestMethod]
        public void UowScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InUnitOfWorkScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<UnitOfWorkScopeLifecycle>();
            }
        }

        [TestMethod]
        public void WeakReferenceScopeTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).InWeakReferenceScope();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldBeType<WeakReferenceScopeLifecycle>();
            }
        }

        [TestMethod]
        public void CustomScopeTest()
        {
            using (var injector = GetInjector())
            {
                var scopeLifecycle = new UnitOfWorkScopeLifecycle();
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) }, injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.InScope(scopeLifecycle);

                Action action = () => syntax.InScope(scopeLifecycle);
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.ScopeLifecycle.ShouldEqual(scopeLifecycle);
            }
        }

        [TestMethod]
        public void DefaultActivatorShouldBeNullTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass());

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Activator.ShouldBeNull();
            }
        }


        [TestMethod]
        public void UseReflectionActivatorTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).UseReflectionActivator();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Activator.ShouldBeType<ReflectionActivator>();
            }
        }

#if !NETFX_CORE
        [TestMethod]
        public void UseEmitActivatorTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).UseEmitActivator();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Activator.ShouldBeType<EmitActivator>();
            }
        }
#endif

#if !NOEXPRESSION
        [TestMethod]
        public void UseExpressionActivatorTest()
        {
            using (var injector = GetInjector())
            {
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                binding.ToMethod(context => new SimpleClass()).UseExpressionActivator();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Activator.ShouldBeType<ExpressionActivator>();
            }
        }
#endif

        [TestMethod]
        public void UseCustomActivatorTest()
        {
            using (var injector = GetInjector())
            {
                var activator = new ReflectionActivator();
                var bindingBuilder = new BindingBuilder<object, object, object, object>(new[] { typeof(IEmpty) },
                                                                                        injector);
                IBindingSyntax binding = bindingBuilder;
                var syntax = binding.ToMethod(context => new SimpleClass());
                syntax.UseCustomActivator(activator);

                Action action = () => syntax.UseCustomActivator(activator);
                action.ShouldThrow<InvalidBindingException>();

                var methodBinding = (MethodBinding)bindingBuilder.Build();
                methodBinding.Activator.ShouldEqual(activator);
            }
        }
    }
}